﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

using MyFIFO.Utils;

namespace MyFIFO
{
    public class MyFifo<T>
    {
        /// <summary>
        /// 写位置
        /// </summary>
        private UInt32 m_writePos;
        /// <summary>
        /// 读位置
        /// </summary>
        private UInt32 m_readPos;
        /// <summary>
        /// byte数组
        /// </summary>
        private byte[] m_buffer;
        /// <summary>
        /// fifo数组字节容量
        /// </summary>
        private UInt32 m_ByteCapacity;

        /// <summary>
        /// 单个数据包大小
        /// </summary>
        private UInt32 m_unitSize;

        /// <summary>
        /// fifo
        /// </summary>
        /// <param name="unitSize">单个包大小</param>
        /// <param name="units">总容量</param>
        public MyFifo(UInt32 unitSize, UInt32 units)
        {
            this.m_unitSize = unitSize;
            this.m_writePos = 0;
            this.m_readPos = 0;
            this.m_ByteCapacity = units * unitSize;
            this.m_buffer = new byte[m_ByteCapacity];
        }
       

        /// <summary>
        /// 当前字节数据量
        /// </summary>
        /// <returns></returns>
        public UInt32 GetSize()
        {
            UInt32 r;
            UInt32 wrpos, rdpos;
            wrpos = m_writePos;
            rdpos = m_readPos;
            if (wrpos >= rdpos) r = wrpos - rdpos;
            else r = m_ByteCapacity - rdpos + wrpos;
            return r;
        }

        /// <summary>
        /// 当包个数
        /// </summary>
        /// <returns></returns>
        public UInt32 GetUnitSize()
        {
            return GetSize()/ this.m_unitSize;
        }

        /// <summary>
        /// 剩余字节大小
        /// </summary>
        /// <returns></returns>
        public UInt32 GetFreeLength()
        {
            UInt32 wrpos, rdpos;
            wrpos = m_writePos;
            rdpos = m_readPos;
            if (rdpos >= wrpos) return (rdpos - wrpos);
            else return (m_ByteCapacity - wrpos + rdpos);
        }

        /// <summary>
        /// 剩余包大小
        /// </summary>
        /// <returns></returns>
        public UInt32 GetFreeUnitLength()
        {
            return GetFreeLength() / this.m_unitSize;
        }

        /// <summary>
        /// 批量读
        /// </summary>
        /// <param name="outbuf"></param>
        /// <param name="maxlen"></param>
        /// <returns></returns>
        public UInt32 Fifo_Read(ref byte[] outbuf, UInt32 maxlen)
        {
            UInt32 i, len, wrpos, rdpos, temp;
            wrpos = m_writePos;
            rdpos = m_readPos;
            if (wrpos >= rdpos) len = wrpos - rdpos;
            else len = m_ByteCapacity - rdpos + wrpos;
            //剩余数据长度比要读的多
            if (len > maxlen) len = maxlen;
            //-----rdpos--------wrpos------
            if ((rdpos + len) <= m_ByteCapacity)
            {
                for (i = 0; i < len; i++) outbuf[i] = m_buffer[i + rdpos];
                rdpos += len;
                if (rdpos + len == m_ByteCapacity) rdpos = 0;
            }
            //--------wrpos----rdpos--
            else
            {
                temp = m_ByteCapacity - rdpos;
                for (i = 0; i < temp; i++) outbuf[i] = m_buffer[i + rdpos];
                for (i = 0; i < (len - (m_ByteCapacity - rdpos)); i++) outbuf[i + temp] = m_buffer[i];
                rdpos = len - (m_ByteCapacity - rdpos);
            }
            m_readPos = rdpos;
            return len;
        }

        /// <summary>
        /// 批量写
        /// </summary>
        /// <param name="inbuf"></param>
        /// <param name="maxlen"></param>
        /// <returns></returns>
        public UInt32 Fifo_Write(byte[] inbuf, UInt32 maxlen)
        {
            UInt32 i, wrpos, rdpos, templen;
            wrpos = m_writePos;
            rdpos = m_readPos;
            if (wrpos >= rdpos) templen = m_ByteCapacity - wrpos + rdpos;
            else templen = rdpos - wrpos;
            if (maxlen < templen) templen = maxlen;
            if ((wrpos + templen) > m_ByteCapacity)
            {
                for (i = 0; i < (m_ByteCapacity - wrpos); i++) m_buffer[wrpos + i] = inbuf[i];
                for (i = 0; i < (templen - (m_ByteCapacity - wrpos)); i++) m_buffer[i] = inbuf[i + m_ByteCapacity - wrpos];
                wrpos = templen - (m_ByteCapacity - wrpos);
            }
            else if ((wrpos + templen) == m_ByteCapacity)
            {
                for (i = 0; i < templen; i++) m_buffer[i + wrpos] = inbuf[i];
                wrpos = 0;
            }
            else
            {
                for (i = 0; i < templen; i++) m_buffer[i + wrpos] = inbuf[i];
                wrpos += templen;
            }
            m_writePos = wrpos;
            return templen;
        }


        /// <summary>
        /// 写入一个数据包
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public bool PushUnit(T t)
        {
            byte[] bs = StructConvertUtils.StructToBytes(t);
            this.Fifo_Write(bs,this.m_unitSize);
            return true;
        }


        /// <summary>
        /// 读出一个数据包
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public bool PopUnit(ref T t)
        {
            byte[] outBuffer = new byte[this.m_unitSize];
            UInt32 len= this.Fifo_Read(ref outBuffer, this.m_unitSize);
            if (len == 0)
            {
                return false;
            }
            object o= StructConvertUtils.ByteToStruct(outBuffer, typeof(T));
            t = (T)o;
            return true;
        }


        public int PopUnit(ref List<T> t,int count)
        {
            int totalCount = 0;
            for(int i = 0; i < count; i++)
            {
             
                byte[] outBuffer = new byte[this.m_unitSize];
                UInt32 len = this.Fifo_Read(ref outBuffer, this.m_unitSize);
                if (len == 0)
                {
                    break;
                }
                object o = StructConvertUtils.ByteToStruct(outBuffer, typeof(T));
                 t.Add((T)o);
                totalCount++;
            }
            return totalCount;
        }


    }

    public static class MyFiFoTest
    {

        public static void Test()
        {
            Stopwatch stopwatch1 = new Stopwatch();
            stopwatch1.Start();


            MyFifo<ChannelTransmitMsg> queue = new MyFifo<ChannelTransmitMsg>((UInt32)(Marshal.SizeOf(typeof(ChannelTransmitMsg))), 5);//自己的集合

            //直接操作byte数组
            if (false)
            {
                byte[] buf = new byte[100];
                buf[2] = 10;
                queue.Fifo_Write(buf, 5);
                byte a = 0;
                byte[] outBuffer = new byte[10];
                queue.Fifo_Read(ref outBuffer, 5);
                Console.WriteLine(a);
            }
            ChannelTransmitMsg chanelMsg2=new ChannelTransmitMsg(1,2,3,4);

           
            queue.PushUnit(chanelMsg2);
            queue.PushUnit(chanelMsg2);

            queue.PopUnit(ref chanelMsg2);
            queue.PopUnit(ref chanelMsg2);
            queue.PopUnit(ref chanelMsg2);

            Console.WriteLine(chanelMsg2);



        }


    }

}